package com.ifp.opengate.boot.filter;

import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;

import java.util.*;
import java.util.Map.Entry;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import com.ifp.adapter.common.AdapterCanstants;
import com.ifp.core.context.ClogicContext;
import com.ifp.core.log.Trace;
import com.ifp.core.util.StringUtil;
import com.ifp.opengate.boot.constants.Constants;
import com.ifp.opengate.boot.emum.ErrorEnum;
import com.ifp.opengate.boot.exception.AccessControlException;
import com.ifp.opengate.boot.task.ConnectorCacheDataTask;
import com.ifp.opengate.boot.utils.LoadBalanceUtils;
import com.ifp.opengate.boot.utils.RequestUtil;

import reactor.core.publisher.Mono;

/**
 * @Classname LoadBalanceFilter
 * @Description TODO
 * @Date 2019/7/17 15:12
 * @Created by zzh
 *          <p>
 *          zhuzh@belink.com
 *          </p>
 * @Version 1.0
 */
@Component
public class LoadBalanceFilter implements GlobalFilter, Ordered {

    @Autowired
    private ConnectorCacheDataTask connectorCacheDataTask;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String actionId = RequestUtil.getActionId(exchange.getRequest());
        ClogicContext clogicContext = (ClogicContext)exchange.getAttributes().get(Constants.CL_CONTEXT);
        Map<String, Object> dataMap = clogicContext.getDataMap();
        Map<String, Object> headerMap = (Map<String, Object>)dataMap.get(AdapterCanstants.HEADER);
        String version = String.valueOf(headerMap.get(Constants.VERSION));

        Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
        Trace.logInfo(Trace.MODULE_COMMON, "匹配的路由ID为：{}", route.getId());
        if (!connectorCacheDataTask.getRouteOfConnectorMap().isEmpty()) {
            Map<Object, Object> connectorMap = connectorCacheDataTask.getRouteOfConnectorMap()
                .get(Constants.OPEN_GATE_API_CONNECTOR + actionId + Constants.BOTTOM_LINE + version);
            Trace.logInfo(Trace.MODULE_ACTION, "actionId-->" + actionId);
            if (null != connectorMap && !connectorMap.isEmpty()) {
                // 静态路由
                String roundRobin = LoadBalanceUtils.RoundRobin(connectorMap);
                exchange.getAttributes().put("connector", roundRobin);
                exchange.getAttributes().put("connectorMap", connectorMap);
                exchange.getAttributes().put("schema", connectorMap.get(roundRobin));
                Trace.logInfo(Trace.MODULE_ACTION, "schema-->" + connectorMap.get(roundRobin));
                Trace.logInfo(Trace.MODULE_ACTION, "使用静态路由=======");
            } else {
                // 动态路由
                connectorMap = connectorCacheDataTask.getRouteOfConnectorMap()
                    .get(Constants.OPEN_GATE_ROUTE_COMM_CACHE_NAME + route.getId());
                List<Entry<String, String>> list = valueUpSort(connectorMap);
                ServerHttpRequest request = exchange.getRequest();
                out:
                for (Entry<String, String> entry : list) {
                    Map<Object, Object> commRuleMap =
                        connectorCacheDataTask.getRouteOfConnectorMap().get(entry.getKey());

                    String connector = (String)commRuleMap.get("commService");
                    exchange.getAttributes().put("connector", connector);
                    Map connMap = new HashMap();
                    connMap.put(connector, commRuleMap.get("protocol"));
                    exchange.getAttributes().put("connectorMap", connMap);
                    exchange.getAttributes().put("schema", commRuleMap.get("protocol"));
                    Trace.logInfo(Trace.MODULE_ACTION, "使用动态路由=======");
                    break out;
                }
            }
        }
        return chain.filter(exchange);
    }

    public List valueUpSort(Map map) {
        if (map == null) {
            return new ArrayList<Map<String, String>>();
        }
        // 默认情况，TreeMap按key升序排序

        // 升序比较器
        Comparator<Entry<String, String>> valueComparator = new Comparator<Entry<String, String>>() {
            @Override
            public int compare(Entry<String, String> o1, Entry<String, String> o2) {
                // TODO Auto-generated method stub
                return Integer.parseInt(o1.getValue()) - Integer.parseInt(o2.getValue());
            }

        };

        // map转换成list进行排序
        List<Entry<String, String>> list = new ArrayList<Entry<String, String>>(map.entrySet());

        // 排序
        Collections.sort(list, valueComparator);
        /*for (Map.Entry<String, Integer> entry : list) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }*/
        return list;
    }

    public boolean compare(String key, String symbol, String value) {
        int keyInt = Integer.parseInt(key);
        int valueInt = Integer.parseInt(value);
        switch (symbol) {
            case "==":
                return keyInt == valueInt;
            case ">":
                return keyInt > valueInt;
            case "<":
                return keyInt < valueInt;
            case ">=":
                return keyInt >= valueInt;
            case "<=":
                return keyInt <= valueInt;
            case "!=":
                return keyInt != valueInt;
            default:
                return false;
        }

    }

    private String getAndCheckParam(ServerHttpRequest request, ClogicContext clogicContext, String key, String desc) {
        String value = getValueByKey(request, clogicContext, key);
        // appId必须存在
        if (!StringUtil.hasText(value)) {
            throw new AccessControlException(ErrorEnum.paramNoFoundError.code(),
                String.format(ErrorEnum.paramNoFoundError.msg(), desc));
        }
        return value;
    }

    private String getValueByKey(ServerHttpRequest request, ClogicContext clogicContext, String key) {
        String value = (String)clogicContext.getValue(key);
        if (!StringUtil.hasText(value)) {
            value = this.getHeaderValue(request, key);
        }
        if (!StringUtil.hasText(value)) {
            value = this.getParamValue(request, key);
        }
        return value;
    }

    public String getParamValue(ServerHttpRequest request, String key) {
        if (request.getQueryParams().containsKey(key)) {
            return request.getQueryParams().getFirst(key);
        }
        return null;
    }

    public String getHeaderValue(ServerHttpRequest request, String key) {
        if (request.getHeaders().containsKey(key)) {
            return request.getHeaders().getFirst(key);
        }
        return null;
    }

    @Override
    public int getOrder() {
        return -9;
    }
}
